﻿using System;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Text;

namespace ClassDataExport
{
    /// <summary>
    /// Excelexport, erstellen von Exceldateien
    /// </summary>
    public class ExcelExport
    {
        #region static
        private static ExcelExport _excelExport = null;
        /// <summary>
        /// Gibt eine Instanz von Excelexport zurück
        /// </summary>
        public static ExcelExport GetExcelExport()
        {
            if (_excelExport == null)
            {
                _excelExport = new ExcelExport();
            }

            return _excelExport;
        }
        #endregion

        #region Properties
        /// <summary>
        /// Fehlermeldungen oder Message werden hier hinterlegt.
        /// </summary>
        public string Message { get; set; }
        private string ConnectionString { get; set; }
        private DataTable DatenTabelle { get; set; }
        #endregion

        #region Konstruktor
        public ExcelExport()
        {
            Message = string.Empty;
            ConnectionString = string.Empty;
        }
        #endregion


        #region Functions/Subs
        /// <summary>
        /// Erstellt eine Exceldatei mit dem Inhalt aus der DataTable in dem übergebenen Pfad inkl. Dateiname
        /// </summary>
        /// <param name="dataTable">DataTable mit den Daten die ins Excel File übernommen werden sollen</param>
        /// <param name="filePath">Dateiname inkl. FilePath wo die Excel Datei abgelegt werden soll</param>
        /// <returns>TRUE->Erstellen erfolgreich | FALSE->Fehler beim Erstellen</returns>
        public Boolean CreateExcelFileFromDataTable(DataTable dataTable, string filePath)
        {
            Message = string.Empty;
            DatenTabelle = dataTable;

            //beide Parameter müssen übergeben werden und es muss ein Wert enthalten sein.
            if (dataTable != null && !string.IsNullOrEmpty(filePath))
            {
                //Wenn kein Tabellenname de DataTable hinterlegt wurde diesen hier festlegen, der Name
                //der DataTable wird später der Name des Sheets in Excel werden.
                if (dataTable.TableName.Trim().Length == 0)
                {
                    dataTable.TableName = "NewTable";
                }

                if (CreateConnectionString(filePath))
                {
                    try
                    {
                        //OLEDB Connection für eine Excel Datei öffnen um hier Daten hinzuzufügen
                        using (OleDbConnection conn = new OleDbConnection(ConnectionString))
                        {
                            conn.Open();

                            //Erstellen des DataSheets in der Excel Datei
                            if (CreateTable(conn))
                            {
                                //Einfügen der Daten in das Excel Sheet
                                return InsertData(conn);
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        SQDebugging.Debugging.WriteLog("Fehler beim Aufbauen der Verbindung.", exception);
                        Message = SQDebugging.Debugging.GetStackTrace(exception);
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Erstellt den passenden Connectionstring
        /// </summary>
        /// <param name="filePath">FilePfad inkl. Dateiname</param>
        /// <returns>TRUE->Erstellen erfolgreich |FALSE->Fehler beim Erstellen</returns>
        private Boolean CreateConnectionString(string filePath)
        {
            if (!Directory.Exists(Path.GetDirectoryName(filePath)))
            {
                Message = string.Format("Der Pfad {0} konnte nicht gefunden werden.\r\n", Path.GetDirectoryName(filePath));
                return false;
            }

            //Erst prüfen um welchen ExcelTyp es sich handelt, xls (2003) oder xlsx (2007)
            string dateiTyp = Path.GetExtension(filePath).Trim('.');

            //Den jeweiligen passenden Connectionstring für die richtige Excelformat zusammenbauen.
            switch (dateiTyp.ToLower())
            {
                case "xls":
                    ConnectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0; Data Source= {0};Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=1\"", filePath);
                    break;
                case "xlsx":
                    ConnectionString = string.Format("Provider=Microsoft.ACE.OLEDB.12.0; Data Source= {0};Extended Properties=\"Excel 12.0 Xml;HDR=YES\"", filePath);
                    break;
                default:
                    SQDebugging.Debugging.WriteLog("Es wurde das falsche Datenformat angegeben was erstellt werden soll, es wird nur 'xlsx' oder 'xsl' unterstützt");
                    Message = "Es wurde das falsche Datenformat angegeben was erstellt werden soll, es wird nur 'xlsx' oder 'xsl' unterstützt\r\n";
                    return false;
            }

            return true;
        }

        /// <summary>
        /// Erstellen der Tabelle (DataSheet) der Exceldatei
        /// </summary>
        /// <param name="conn">Connectionstring, der die Verbindung bereits geöffnet hat.</param>
        /// <returns>TRUE->Erstellen erfolgreich | FALSE->Fehler beim Erstellen</returns>
        private Boolean CreateTable(OleDbConnection conn)
        {
            try
            {
                //zuerst versuchen die Tabelle zu löschen
                DeleteTable(conn);

                //String zusammenbauen der die Tabelle erstellt (den Inhalt)
                StringBuilder createTable = new StringBuilder();
                //"CREATE TABLE [Tabelle6]([Text] Char,[Zahl] Integer,[Datum] Date,[Text2] Char)"
                createTable.Append("CREATE TABLE ").Append(string.Format("[{0}] (", DatenTabelle.TableName));
                StringBuilder dataTypes = new StringBuilder();

                int count = 0;
                foreach (DataColumn eColumn in DatenTabelle.Columns)
                {
                    if (count > 0)
                    {
                        createTable.Append(", ");
                        dataTypes.Append(", ");
                    }

                    createTable.Append("[").Append(eColumn.ColumnName).Append("]").Append(" ").Append(eColumn.DataType.Name);
                    dataTypes.Append("[").Append(eColumn.DataType).Append("]");
                    count++;
                }

                createTable.Append(")");

                //Erstellen der Tabelle für die Excel file.
                OleDbCommand cmd = new OleDbCommand(createTable.ToString(), conn);
                cmd.ExecuteNonQuery();


            }
            catch (Exception exception)
            {
                SQDebugging.Debugging.WriteLog("Fehler beim Erstellen der Exceltabelle.", exception);
                Message = SQDebugging.Debugging.GetStackTrace(exception);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Löschen der Tabelle aus dem Excel Sheet, damit die Daten neu geschrieben werden können.
        /// </summary>
        /// <param name="conn">Connection die bereits geöffnet ist.</param>
        private void DeleteTable(OleDbConnection conn)
        {
            try
            {
                string deleteTable = string.Format("DROP TABLE [{0}]", DatenTabelle.TableName);
                //Einfügen der Daten für eine Zeile in der Excel Tabelle.
                OleDbCommand cmd = new OleDbCommand(deleteTable, conn);
                cmd.ExecuteNonQuery();
            }
            catch (Exception exception)
            {
                SQDebugging.Debugging.WriteLog(exception);
                Message = string.Format("Fehler beim Löschen der Tabelle {0}", DatenTabelle.TableName);    
            }
        }

        /// <summary>
        /// Einfügen von Daten in das DataSheet
        /// </summary>
        /// <param name="conn">Connection die bereits geöffnet ist.</param>
        /// <returns>TRUE->Einfügen der Daten erfolgreich | FALSE->Fehler beim Einfügen der Daten</returns>
        private Boolean InsertData(OleDbConnection conn)
        {
            try
            {
                //Einfügen des Inhaltes in die Excelfile, zuerst den String erstellen für das Einfügen der Daten in die Tabelle.
                //INSERT INTO Daten VALUES (WERT1, WERT2);
                //Alle Rows der DataTable durchlaufen um den string zusammenzubauen der die Daten einfügt in die Excel Tabelle
                foreach (DataRow eRow in DatenTabelle.Rows)
                {
                    StringBuilder addData = new StringBuilder();
                    addData.Append("INSERT INTO [").Append(DatenTabelle.TableName).Append("] VALUES (");
                    int count = 0;

                    //Alle Spalten der Row durchgehen, um die Daten von jeder Spalte dem String hinzuzufügen
                    foreach (DataColumn eColumn in eRow.Table.Columns)
                    {
                        if (count > 0)
                        {
                            addData.Append(", ");
                        }

                        addData.Append("\"").Append(eRow[eColumn.ColumnName].ToString()).Append("\"");
                        count++;
                    }

                    addData.Append(")");

                    //Einfügen der Daten für eine Zeile in der Excel Tabelle.
                    OleDbCommand cmd = new OleDbCommand(addData.ToString(), conn);
                    cmd.ExecuteNonQuery();
                }
            }
            catch (Exception exception)
            {
                SQDebugging.Debugging.WriteLog("Fehler beim Hinzufügen der Daten zur Tabelle.", exception);
                Message = SQDebugging.Debugging.GetStackTrace(exception);
                return false;
            }

            return true;
        }

        #endregion
    }
}
